package io.gitee.caoxiaoyu97.lifecycle.lifecyle.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.AdaptiveRecvByteBufAllocator;
import io.netty.channel.ChannelDuplexHandler;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.GlobalEventExecutor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*********************************************************
 * 文件名称：AbstractServer.java
 * 系统名称：交易银行系统V2.0
 * 模块名称：com.hundsun.ifts.nbyh.pre.server
 * 功能说明：服务顶层
 * 开发人员 @author：caoxy31978
 * 开发时间 @date：2022/8/25 8:31
 * 修改记录：程序版本  修改日期  修改人员  修改单号  修改说明
 *********************************************************/
public abstract class AbstractServer {

    private static Logger LOGGER;
    /**
     * 监听线程组
     */
    EventLoopGroup bossGroup;
    /**
     * 工作线程组
     */
    EventLoopGroup workerGroup;
    /**
     * 启动程序
     */
    ServerBootstrap bootstrap;
    /**
     *
     */
    ChannelFuture future;
    protected final ChannelGroup channelGroup = new DefaultChannelGroup(
            GlobalEventExecutor.INSTANCE
    );
    /**
     * 处理器
     * new HostSocketEncode(), new HostSocketDecode(), new FileSocketHandler()
     * @return
     */
    public abstract ChannelHandler[] getHandler();

    /**
     * 服务ip
     * @return
     */
    public abstract int getPort();

    /**
     * 超时时间 单位秒
     * @return
     */
    public int getTimeOut(){
        return 180;
    }

    /**
     * 服务端口
     * @return
     */
    public String getSerVerName(){
        return this.getClass().getTypeName();
    };

    public AbstractServer(){
        LOGGER = LoggerFactory.getLogger(this.getClass());

        bossGroup = new NioEventLoopGroup();
        workerGroup = new NioEventLoopGroup();
        bootstrap = new ServerBootstrap();
        //绑定线程池
        bootstrap.group(bossGroup, workerGroup)
                // 指定使用的channel
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.RCVBUF_ALLOCATOR, new AdaptiveRecvByteBufAllocator(64, 99999, 99999))
                // 绑定客户端连接时候触发操作
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    @Override
                    protected void initChannel(SocketChannel ch) {
                        LOGGER.info("================A.收到客户端链接到{}服务器, IP为：{}========================",getSerVerName(),ch.id().asShortText() + ch.remoteAddress());
                        channelGroup.add(ch);
                        //设置读写超时为180秒
                        ch.pipeline().addLast(new IdleStateHandler(0,0,getTimeOut()));
                        // 读写超时则关闭连接
                        ch.pipeline().addLast(new ChannelDuplexHandler(){
                            @Override
                            public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
                                if (evt instanceof IdleStateEvent){
                                    ctx.close();
                                }
                            }
                        });
                        ch.pipeline().addLast(getHandler());
                    }
                });
    }
    /**
     * SOCKET监听
     *
     * @throws InterruptedException
     */
    public void start(){
        LOGGER.info("================1.{} Server 启动中，端口为：{}========================",getSerVerName(),getPort());
        // 服务器异步创建绑定
        try {
            this.future = bootstrap.bind(getPort()).sync();
        } catch (InterruptedException e) {
            throw new RuntimeException("启动服务失败",e);
        }
        LOGGER.info("================2.{} Server 启动完毕！！========================",getSerVerName());
    }
    public void stop(){
        LOGGER.info("================{} Server 关闭中中，端口为：{}========================",getSerVerName(),getPort());
        try {
            bossGroup.shutdownGracefully().sync();
        }catch (Exception e){
            LOGGER.error("关闭bossGroup异常",e);
        }
        awaitRunBusiness();
        try {
            workerGroup.shutdownGracefully().sync();
        }catch (Exception e){
            LOGGER.error("关闭workerGroup异常",e);
        }
        LOGGER.info("================2{} Server 关闭结束！！========================",getSerVerName());
    }

    private void awaitRunBusiness(){
        long maxWaitMillis = (getTimeOut() * 2 + 10) * 1000L;
        long startTime = System.currentTimeMillis();
        while (!channelGroup.isEmpty()) {
            long elapsed = System.currentTimeMillis() - startTime;
            if (elapsed >= maxWaitMillis) {
                break;
            }
            LOGGER.info("仍有 {} 个连接，最多再等待 {} 秒...", channelGroup.size(), (maxWaitMillis - elapsed) / 1000);
            // 避免忙等
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                LOGGER.error("等待连接关闭异常", e);
            }
        }
        if (channelGroup.isEmpty()) {
            LOGGER.info("所有连接已关闭");
        }else{
            LOGGER.warn("等待连接关闭超时 ({}秒)，强制继续。仍有 {} 个连接。", maxWaitMillis / 1000, channelGroup.size());
        }
    }
}
